/**
 * @file risk_manager.h
 * @brief 风险管理器的定义
 * 
 * @details 该文件定义了风险管理器（RiskManager）类和风险信息（RiskInfo）结构体，
 * 用于计算和检查所有交易工具的风险。风险检查包括订单大小、仓位大小和交易损失等。
 * 这些组件在交易系统中负责确保交易在安全的风险参数范围内进行。
 * 
 * @author 原作者
 * @date 2023
 */

#pragma once

#include "common/macros.h"
#include "common/logging.h"

#include "position_keeper.h"
#include "om_order.h"

using namespace Common;

/**
 * @namespace Trading
 * @brief 交易相关的命名空间
 * 
 * @details 包含交易系统的核心组件，如订单、仓位管理器、风险管理器等。
 */
namespace Trading {
  // 前向声明
  class OrderManager;

  /**
   * @enum RiskCheckResult
   * @brief 风险检查结果的枚举
   * 
   * @details 该枚举捕获风险检查的结果。ALLOWED表示通过了所有风险检查，
   * 其他值表示失败的原因，如订单过大、仓位过大或损失过大。
   */
  enum class RiskCheckResult : int8_t {
    /**
     * @brief 无效的风险检查结果
     * 
     * @details 表示风险检查结果无效或未初始化。
     */
    INVALID = 0,
    
    /**
     * @brief 订单过大
     * 
     * @details 表示订单数量超过了允许的最大订单大小。
     */
    ORDER_TOO_LARGE = 1,
    
    /**
     * @brief 仓位过大
     * 
     * @details 表示执行该订单后的仓位大小将超过允许的最大仓位大小。
     */
    POSITION_TOO_LARGE = 2,
    
    /**
     * @brief 损失过大
     * 
     * @details 表示当前的总盈亏低于允许的最大损失限制。
     */
    LOSS_TOO_LARGE = 3,
    
    /**
     * @brief 允许交易
     * 
     * @details 表示订单通过了所有风险检查，允许执行交易。
     */
    ALLOWED = 4
  };

  /**
   * @brief 将风险检查结果枚举转换为字符串
   * 
   * @details 将 RiskCheckResult 枚举值转换为其对应的字符串表示。
   * 主要用于调试、日志记录和错误报告。
   * 
   * @param result 要转换的风险检查结果枚举值
   * @return 风险检查结果的字符串表示，如果结果不识别则返回空字符串
   */
  inline auto riskCheckResultToString(RiskCheckResult result) {
    switch (result) {
      case RiskCheckResult::INVALID:
        return "INVALID";
      case RiskCheckResult::ORDER_TOO_LARGE:
        return "ORDER_TOO_LARGE";
      case RiskCheckResult::POSITION_TOO_LARGE:
        return "POSITION_TOO_LARGE";
      case RiskCheckResult::LOSS_TOO_LARGE:
        return "LOSS_TOO_LARGE";
      case RiskCheckResult::ALLOWED:
        return "ALLOWED";
    }

    return "";
  }

  /**
   * @struct RiskInfo
   * @brief 单个交易工具的风险信息结构体
   * 
   * @details 该结构体表示单个交易工具进行风险检查所需的信息。
   * 它包含仓位信息和风险配置，用于进行交易前的风险检查。
   * 风险检查包括订单大小、仓位大小和交易损失等。
   */
  struct RiskInfo {
    /**
     * @brief 仓位信息指针
     * 
     * @details 指向当前交易工具的仓位信息对象的指针。
     * 用于获取当前仓位大小和盈亏信息，以进行风险检查。
     */
    const PositionInfo *position_info_ = nullptr;

    /**
     * @brief 风险配置
     * 
     * @details 当前交易工具的风险配置，包括最大订单大小、最大仓位大小和最大损失等参数。
     * 这些参数用于定义风险检查的阈值。
     */
    RiskCfg risk_cfg_;

    /**
     * @brief 检查风险，判断是否允许发送指定侧向和数量的订单
     * 
     * @details 该方法执行交易前的风险检查，包括以下检查：
     * 1. 订单大小检查：确保订单数量不超过最大允许的订单大小
     * 2. 仓位大小检查：确保执行该订单后的仓位不超过最大允许的仓位大小
     * 3. 损失检查：确保当前的总盈亏不低于最大允许的损失
     * 
     * @param side 订单侧向（买入或卖出）
     * @param qty 订单数量
     * @return 风险检查结果，表示是否允许发送订单或失败的原因
     */
    auto checkPreTradeRisk(Side side, Qty qty) const noexcept {
      // check order-size
      if (UNLIKELY(qty > risk_cfg_.max_order_size_))
        return RiskCheckResult::ORDER_TOO_LARGE;
      if (UNLIKELY(std::abs(position_info_->position_ + sideToValue(side) * static_cast<int32_t>(qty)) > static_cast<int32_t>(risk_cfg_.max_position_)))
        return RiskCheckResult::POSITION_TOO_LARGE;
      if (UNLIKELY(position_info_->total_pnl_ < risk_cfg_.max_loss_))
        return RiskCheckResult::LOSS_TOO_LARGE;

      return RiskCheckResult::ALLOWED;
    }

    /**
     * @brief 将风险信息转换为字符串表示
     * 
     * @details 生成风险信息的文本表示，包括仓位信息和风险配置。
     * 这个方法主要用于调试和日志记录。
     * 
     * @return 风险信息的字符串表示
     */
    auto toString() const {
      std::stringstream ss;
      ss << "RiskInfo" << "["
         << "pos:" << position_info_->toString() << " "
         << risk_cfg_.toString()
         << "]";

      return ss.str();
    }
  };

  /**
   * @typedef TickerRiskInfoHashMap
   * @brief 从证券标识符到风险信息的映射
   * 
   * @details 使用固定大小的数组实现的哈希表，用于存储和管理所有交易工具的风险信息。
   * 数组大小由 ME_MAX_TICKERS 常量决定，表示系统支持的最大证券数量。
   */
  typedef std::array<RiskInfo, ME_MAX_TICKERS> TickerRiskInfoHashMap;

  /**
   * @class RiskManager
   * @brief 顶级风险管理器类，用于计算和检查所有交易工具的风险
   * 
   * @details 该类管理所有交易工具的风险信息，并提供交易前的风险检查功能。
   * 风险管理器与仓位管理器密切协作，确保交易在安全的风险参数范围内进行。
   * 它对每个交易工具的订单大小、仓位大小和交易损失进行限制。
   */
  class RiskManager {
  public:
    /**
     * @brief 风险管理器的构造函数
     * 
     * @details 初始化风险管理器，设置日志记录器、仓位管理器和交易引擎配置。
     * 对每个交易工具初始化风险信息，包括设置仓位信息指针和风险配置。
     * 
     * @param logger 日志记录器指针，用于记录风险管理器的操作和状态
     * @param position_keeper 仓位管理器指针，用于获取各交易工具的仓位信息
     * @param ticker_cfg 交易引擎配置映射，包含各交易工具的风险配置
     */
    RiskManager(Common::Logger *logger, const PositionKeeper *position_keeper, const TradeEngineCfgHashMap &ticker_cfg);

    /**
     * @brief 检查指定证券的交易前风险
     * 
     * @details 对指定证券的指定侧向和数量的订单进行风险检查。
     * 该方法将调用相应证券的RiskInfo对象的checkPreTradeRisk方法来执行实际的风险检查。
     * 
     * @param ticker_id 证券标识符
     * @param side 订单侧向（买入或卖出）
     * @param qty 订单数量
     * @return 风险检查结果，表示是否允许发送订单或失败的原因
     */
    auto checkPreTradeRisk(TickerId ticker_id, Side side, Qty qty) const noexcept {
      return ticker_risk_.at(ticker_id).checkPreTradeRisk(side, qty);
    }

    /**
     * @brief 删除的默认构造函数、拷贝构造函数、移动构造函数和赋值运算符
     * 
     * @details 风险管理器类不允许默认构造、拷贝或移动，以确保其安全使用。
     * 这些操作被显式删除，以防止意外创建多个风险管理器实例或意外复制。
     */
    RiskManager() = delete;

    /**
     * @brief 删除的拷贝构造函数
     */
    RiskManager(const RiskManager &) = delete;

    /**
     * @brief 删除的移动构造函数
     */
    RiskManager(const RiskManager &&) = delete;

    /**
     * @brief 删除的拷贝赋值运算符
     */
    RiskManager &operator=(const RiskManager &) = delete;

    /**
     * @brief 删除的移动赋值运算符
     */
    RiskManager &operator=(const RiskManager &&) = delete;

  private:
    /**
     * @brief 时间字符串缓存
     * 
     * @details 用于在日志记录中存储当前时间的字符串表示，避免重复创建。
     */
    std::string time_str_;
    
    /**
     * @brief 日志记录器指针
     * 
     * @details 用于记录风险管理器的操作和状态的日志记录器。
     */
    Common::Logger *logger_ = nullptr;

    /**
     * @brief 从证券标识符到风险信息的映射容器
     * 
     * @details 使用哈希表存储和管理所有交易工具的风险信息。
     * 这是风险管理器的核心数据结构，存储了所有管理的风险信息。
     */
    TickerRiskInfoHashMap ticker_risk_;
  };
}
